package data

import (
	"context"
	"fmt"
	"time"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"

	"gitee.com/danqingzhao/go-dango/apps/user/biz"
	"gitee.com/danqingzhao/go-dango/pkg/proto/auth"
	"gitee.com/danqingzhao/go-dango/pkg/proto/settings"
	"gitee.com/danqingzhao/go-dango/pkg/third/orm"
)

type customerRepo struct {
	data *Data
	log  *log.Helper
}

func NewUserRepo(data *Data, logHelpers *settings.LogHelpers) biz.CustomerRepo {
	return &customerRepo{data: data, log: logHelpers.Data}
}

func (r *customerRepo) CreateModel(ctx context.Context, m *biz.CustomerModel) error {
	if err := orm.DBCreate(ctx, r.data.gormDB, &biz.CustomerModel{}, m); err != nil {
		r.log.WithContext(ctx).Error(err.Error())
		return err
	}
	return nil
}

func (r *customerRepo) UpdateModel(ctx context.Context, m biz.CustomerModel, conds ...any) error {
	if err := orm.DBUpdate(ctx, r.data.gormDB, &m, &biz.CustomerModel{}, nil, conds...); err != nil {
		r.log.WithContext(ctx).Error(err.Error())
		return err
	}
	return nil
}

func (r *customerRepo) DeleteModel(ctx context.Context, conds ...any) error {
	if err := orm.DBDelete(ctx, r.data.gormDB, &biz.CustomerModel{}, conds...); err != nil {
		r.log.WithContext(ctx).Error(err.Error())
		return err
	}
	return nil
}

func (r *customerRepo) FindModel(
	ctx context.Context,
	preloads []string,
	conds ...any,
) (*biz.CustomerModel, error) {
	var m biz.CustomerModel
	if err := orm.DBFind(ctx, r.data.gormDB, preloads, &m, conds...); err != nil {
		r.log.WithContext(ctx).Error(err.Error())
		return nil, err
	}
	return &m, nil
}

func (r *customerRepo) ListModel(
	ctx context.Context,
	qp orm.QueryParams,
) (int64, []biz.CustomerModel, error) {
	var ms []biz.CustomerModel
	count, err := orm.DBList(ctx, r.data.gormDB, &biz.CustomerModel{}, &ms, qp)
	if err != nil {
		r.log.WithContext(ctx).Error(err.Error())
		return 0, nil, err
	}
	return count, ms, nil
}

func (r *customerRepo) geneJWTClaims(expTime time.Duration) jwt.RegisteredClaims {
	nowTime := time.Now()
	expiresAt := nowTime.Add(time.Minute * expTime)
	return jwt.RegisteredClaims{
		Issuer:    httpAddr,
		ExpiresAt: jwt.NewNumericDate(expiresAt),
		NotBefore: jwt.NewNumericDate(nowTime),
		IssuedAt:  jwt.NewNumericDate(nowTime),
		ID:        uuid.NewString(),
	}
}

func (r *customerRepo) GenerateToken(
	subject string,
	ecs auth.ExtendClaims,
) (string, error) {
	var jwtClaims jwt.RegisteredClaims
	if subject == auth.JWTSubject_ACCESS.String() {
		jwtClaims = r.geneJWTClaims(AccessTokenExpireMinutes)
	} else if subject == auth.JWTSubject_REFRESH.String() {
		jwtClaims = r.geneJWTClaims(RefreshTokenExpireMinutes)
	} else {
		return "", fmt.Errorf("invalid subject: %s", subject)
	}
	jwtClaims.Subject = subject
	uc := auth.UserClaims{
		RegisteredClaims: jwtClaims,
		ExtendClaims:     ecs,
	}
	token, err := auth.GenerateToken(uc)
	if err != nil {
		r.log.Error(err.Error())
		return "", err
	}
	return token, nil
}
